use std::{ops::*, fmt::Display};

#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Vector3f {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

impl Vector3f {
    pub fn new(x: f32, y: f32, z: f32) -> Self {
        Self { x, y, z }
    }

    pub fn dot_product(&self, rhs: &Vector3f) -> f32 {
        self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
    }

    pub fn cross_product(&self, rhs: &Vector3f) -> Self {
        Self {
            x: self.y * rhs.z - self.z * rhs.y,
            y: self.z * rhs.x - self.x * rhs.z,
            z: self.x * rhs.y - self.y * rhs.x,
        }
    }

    pub fn normalize(self) -> Self {
        let mag2 = self.dot_product(&self);
        if mag2 > 0. {
            let inv_mag = 1.0 / mag2.sqrt();
            Self {
                x: self.x * inv_mag,
                y: self.y * inv_mag,
                z: self.z * inv_mag,
            }
        } else {
            // zero vector
            self
        }
    }

    pub fn lerp(a: Vector3f, b: Vector3f, t: f32) -> Self {
        a * (1.0 - t) + b * t
    }
}

impl Mul<f32> for Vector3f {
    type Output = Vector3f;

    fn mul(self, rhs: f32) -> Self::Output {
        let x = self.x * rhs;
        let y = self.y * rhs;
        let z = self.z * rhs;
        Self { x, y, z }
    }
}

impl Div<f32> for Vector3f {
    type Output = Self;

    fn div(self, rhs: f32) -> Self::Output {
        let x = self.x / rhs;
        let y = self.y / rhs;
        let z = self.z / rhs;
        Self { x, y, z }
    }
}

impl Mul for Vector3f {
    type Output = Self;

    fn mul(self, rhs: Self) -> Self::Output {
        let x = self.x * rhs.x;
        let y = self.y * rhs.y;
        let z = self.z * rhs.z;
        Self { x, y, z }
    }
}

impl Sub for Vector3f {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        let x = self.x - rhs.x;
        let y = self.y - rhs.y;
        let z = self.z - rhs.z;
        Self { x, y, z }
    }
}

impl Add for Vector3f {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        let x = self.x + rhs.x;
        let y = self.y + rhs.y;
        let z = self.z + rhs.z;
        Self { x, y, z }
    }
}

impl Neg for Vector3f {
    type Output = Self;

    fn neg(self) -> Self::Output {
        Self::new(-self.x, -self.y, -self.z)
    }
}

impl AddAssign for Vector3f {
    fn add_assign(&mut self, rhs: Self) {
        self.x += rhs.x;
        self.y += rhs.y;
        self.z += rhs.z;
    }
}

impl Mul<Vector3f> for f32 {
    type Output = Vector3f;

    fn mul(self, rhs: Vector3f) -> Self::Output {
        Vector3f::new(self * rhs.x, self * rhs.y, self * rhs.z)
    }
}

impl Display for Vector3f {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "vec3f: {}, {}, {}", self.x, self.y, self.z)
    }
}

#[macro_export]
macro_rules! v3f {
    ($x:expr, $y:expr, $z:expr) => {
        Vector3f::new($x, $y, $z)
    };
    ($x:expr) => {
        Vector3f::new($x, $x, $x)
    };
    () => {
        Vector3f::new(0.0, 0.0, 0.0)
    };
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Vector2f {
    pub x: f32,
    pub y: f32,
}

impl Vector2f {
    pub fn new(x: f32, y: f32) -> Self {
        Self { x, y }
    }
}

impl Mul<f32> for Vector2f {
    type Output = Vector2f;

    fn mul(self, rhs: f32) -> Self::Output {
        let x = self.x * rhs;
        let y = self.y * rhs;
        Self { x, y }
    }
}

impl Div<f32> for Vector2f {
    type Output = Self;

    fn div(self, rhs: f32) -> Self::Output {
        let x = self.x / rhs;
        let y = self.y / rhs;
        Self { x, y }
    }
}

impl Mul for Vector2f {
    type Output = Self;

    fn mul(self, rhs: Self) -> Self::Output {
        let x = self.x * rhs.x;
        let y = self.y * rhs.y;
        Self { x, y }
    }
}

impl Sub for Vector2f {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        let x = self.x - rhs.x;
        let y = self.y - rhs.y;
        Self { x, y }
    }
}

impl Add for Vector2f {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        let x = self.x + rhs.x;
        let y = self.y + rhs.y;
        Self { x, y }
    }
}

impl Neg for Vector2f {
    type Output = Self;

    fn neg(self) -> Self::Output {
        Self::new(-self.x, -self.y)
    }
}

impl AddAssign for Vector2f {
    fn add_assign(&mut self, rhs: Self) {
        self.x += rhs.x;
        self.y += rhs.y;
    }
}

impl Mul<Vector2f> for f32 {
    type Output = Vector2f;

    fn mul(self, rhs: Vector2f) -> Self::Output {
        Vector2f::new(self * rhs.x, self * rhs.y)
    }
}

#[macro_export]
macro_rules! v2f {
    ($x:expr, $y:expr) => {
        Vector2f::new($x, $y)
    };
    ($x:expr) => {
        Vector2f::new($x, $x)
    };
    () => {
        Vector2f::new(0.0, 0.0)
    };
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_macro_3f() {
        assert_eq!(Vector3f::new(1.0, 1.0, 1.0), v3f!(1.0));
        assert_eq!(Vector3f::new(1.0, 2.0, 3.0), v3f!(1.0, 2.0, 3.0));
        assert_eq!(Vector3f::new(0.0, 0.0, 0.0), v3f!());
    }

    #[test]
    fn test_ops_3f() {
        assert_eq!(v3f!(1., 2., 3.) * 2., v3f!(2., 4., 6.));
        assert_eq!(2. * v3f!(1., 2., 3.), v3f!(2., 4., 6.));

        assert_eq!(v3f!(1., 2., 3.), v3f!(2., 4., 6.) / 2.);

        assert_eq!(
            v3f!(1., 2., 3.) * v3f!(1., 2., 3.),
            v3f!(1. * 1., 2. * 2., 3. * 3.)
        );
        assert_eq!(v3f!(0., 1., 2.), v3f!() + Vector3f::new(0.0, 1., 2.));
        assert_eq!(v3f!(1., 2., 3.) - v3f!(1., 2., 3.), v3f!());
        assert_eq!(v3f!(1., 2., 3.), -v3f!(-1., -2., -3.));

        let mut x = v3f!(1., 2., 3.);
        x += v3f!(2., 4., 6.);
        assert_eq!(x, v3f!(3., 6., 9.));
    }

    #[test]
    fn test_macro_2f() {
        assert_eq!(Vector2f::new(1.0, 1.0), v2f!(1.0));
        assert_eq!(Vector2f::new(1.0, 2.0), v2f!(1.0, 2.0));
        assert_eq!(Vector2f::new(0.0, 0.0), v2f!());
    }

    #[test]
    fn test_ops_2f() {
        assert_eq!(v2f!(1., 2.) * 2., v2f!(2., 4.));
        assert_eq!(2. * v2f!(1., 2.), v2f!(2., 4.));

        assert_eq!(v2f!(1., 2.), v2f!(2., 4.) / 2.);

        assert_eq!(v2f!(1., 2.) * v2f!(1., 2.), v2f!(1. * 1., 2. * 2.));
        assert_eq!(v2f!(0., 1.), v2f!() + Vector2f::new(0.0, 1.));
        assert_eq!(v2f!(1., 2.) - v2f!(1., 2.), v2f!());
        assert_eq!(v2f!(1., 2.), -v2f!(-1., -2.));

        let mut x = v2f!(1., 2.);
        x += v2f!(2., 4.);
        assert_eq!(x, v2f!(3., 6.));
    }
}
